/*
Copyright 2007 John Tsiombikas <nuclear@siggraph.org>

This file is part of the pixelshow 2007 invitation demo.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ggen.h"
#include "mesh.h"

#define PI		3.1415926535897931
#define TWO_PI	6.2831853071795862
#define HALF_PI	1.5707963267948966

/* macros to convert sph-coords to a cartesian 3d vector */
#define SPH_X(rho, theta, phi)	((rho) * cos(theta) * sin(phi))
#define SPH_Y(rho, theta, phi)	((rho) * cos(phi))
#define SPH_Z(rho, theta, phi)	((rho) * sin(theta) * sin(phi))

#define VERTEX(vx, vy, vz, rad, u, v) \
	do { \
		float r = rad; \
		vptr->pos.x = (vx); vptr->pos.y = (vy); vptr->pos.z = (vz); \
		vptr->norm.x = vptr->pos.x / (r); vptr->norm.y = vptr->pos.y / (r); vptr->norm.z = vptr->pos.z / (r); \
		vptr->texc.x = (u); vptr->texc.y = (v); \
		vptr++; \
	} while(0)

int gen_sphere(struct mesh *m, float rad, int vpoints, float umax, float vmax)
{
	float u0, u1, v0, v1;
	float du, dv;
	int i, j, upoints = vpoints * 2;
	int nfaces = vpoints * upoints * 2;	/* quads to triangles */
	int nverts = nfaces * 3;			/* triangles to vertices */
	struct vertex *varr, *vptr;

	if(!(vptr = varr = malloc(nverts * sizeof *varr))) {
		return -1;
	}

	du = umax / upoints;
	dv = vmax / vpoints;

	v0 = 0.0;
	v1 = dv;

	for(i=0; i<vpoints; i++) {
		float phi0 = v0 * PI;
		float phi1 = v1 * PI;

		u0 = 0.0;
		u1 = du;

		for(j=0; j<upoints; j++) {
			float theta0 = u0 * TWO_PI;
			float theta1 = u1 * TWO_PI;

			VERTEX(SPH_X(rad, theta0, phi0), SPH_Y(rad, theta0, phi0), SPH_Z(rad, theta0, phi0), rad, u0, v0);
			VERTEX(SPH_X(rad, theta1, phi0), SPH_Y(rad, theta1, phi0), SPH_Z(rad, theta1, phi0), rad, u1, v0);
			VERTEX(SPH_X(rad, theta1, phi1), SPH_Y(rad, theta1, phi1), SPH_Z(rad, theta1, phi1), rad, u1, v1);

			VERTEX(SPH_X(rad, theta0, phi0), SPH_Y(rad, theta0, phi0), SPH_Z(rad, theta0, phi0), rad, u0, v0);
			VERTEX(SPH_X(rad, theta1, phi1), SPH_Y(rad, theta1, phi1), SPH_Z(rad, theta1, phi1), rad, u1, v1);
			VERTEX(SPH_X(rad, theta0, phi1), SPH_Y(rad, theta0, phi1), SPH_Z(rad, theta0, phi1), rad, u0, v1);

			u0 += du;
			u1 += du;
		}
		v0 += dv;
		v1 += dv;
	}

	m->varr = varr;
	m->vnum = nverts;
	make_kdtree(m);
	return 0;
}

#define CYL_VERTEX(vx, vy, vz, rad, u, v) \
	do { \
		float r = rad; \
		float phi = atan(height / (r1 - r2)); \
		float theta = (u) * TWO_PI; \
		vptr->pos.x = (vx) * r; vptr->pos.y = (vy); vptr->pos.z = (vz) * r; \
		vptr->norm.x = sin(phi) * cos(theta) - cos(phi) * sin(theta); \
		vptr->norm.y = cos(phi); \
		vptr->norm.z = sin(phi) * sin(theta) + cos(phi) * cos(theta); \
		vptr->texc.x = (u); vptr->texc.y = (v); \
		vptr++; \
	} while(0)


int gen_cylcone(struct mesh *m, float r1, float r2, float height, int upoints, int vsub, float umax, int caps)
{
	float u0, u1, v0, v1;
	float du, dv;
	int i, j, slices, nverts, nfaces;
	struct vertex *vptr, *varr;
	float start_y = -height / 2.0;

	if(upoints < 3) upoints = 3;
	slices = vsub + 2;

	nfaces = slices * upoints * 2;
	nverts = nfaces * 3;

	if(!(vptr = varr = malloc(nverts * sizeof *varr))) {
		return -1;
	}

	du = umax / upoints;
	dv = height / slices;

	v0 = start_y;
	v1 = start_y + dv;

	for(i=0; i<slices; i++) {
		float h0 = v0;
		float h1 = v1;

		u0 = 0.0;
		u1 = du;

		for(j=0; j<upoints; j++) {
			float theta0 = u0 * TWO_PI;
			float theta1 = u1 * TWO_PI;

			float rad1 = r1 + (r2 - r1) * ((v0 + height / 2.0) / height);
			float rad0 = r1 + (r2 - r1) * ((v1 + height / 2.0) / height);

			CYL_VERTEX(cos(theta0), h1, sin(theta0), rad0, u0, v0 / height);
			CYL_VERTEX(cos(theta1), h1, sin(theta1), rad0, u1, v0 / height);
			CYL_VERTEX(cos(theta1), h0, sin(theta1), rad1, u1, v1 / height);

			CYL_VERTEX(cos(theta0), h1, sin(theta0), rad0, u0, v0 / height);
			CYL_VERTEX(cos(theta1), h0, sin(theta1), rad1, u1, v1 / height);
			CYL_VERTEX(cos(theta0), h0, sin(theta0), rad1, u0, v1 / height);

			u0 += du;
			u1 += du;
		}
		v0 += dv;
		v1 += dv;
	}

	m->varr = varr;
	m->vnum = nverts;
	make_kdtree(m);
	if(fabs(r1 - r2) > 0.001 * height) {
		calc_normals(m);
	}
	return 0;
}
